/*
 moxl - Modular XML Library
 Copyright (c) 2007, Malcolm Sparks

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package moxl.xpath.parser.types;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import moxl.dom.MoxlNode;
import moxl.xpath.XPathContext;
import moxl.xpath.axes.InvalidAxisForNodeException;
import moxl.xpath.axes.XPathAxis;

public class Step implements NonTerminal
{
	private XPathAxis axis;
	private NodeTestMatcher nodeTestMatcher;
	private List<Predicate> predicates;

	public XPathAxis getAxis()
	{
		return axis;
	}

	public void setAxis(XPathAxis axis)
	{
		if (this.axis != null)
		{
			throw new IllegalStateException("Axis already set to " + axis);
		}
		this.axis = axis;
	}

	public NodeTestMatcher getNodeTestMatcher()
	{
		return nodeTestMatcher;
	}

	public void setNodeTestMatcher(NodeTestMatcher nodeTestMatcher)
	{
		this.nodeTestMatcher = nodeTestMatcher;
	}

	public List<Predicate> listPredicates()
	{
		if (predicates == null)
		{
			return Collections.emptyList();
		}
		else
		{
			return predicates;
		}
	}

	public void addPredicate(Predicate predicate)
	{
		if (predicates == null)
		{
			predicates = new ArrayList<Predicate>();
		}
		predicates.add(predicate);
	}

	public List<MoxlNode> run(List<MoxlNode> baseSet, XPathContext ctx) throws InvalidAxisForNodeException, XPathEvaluationException
	{
		if (axis == null)
		{
			throw new IllegalStateException("No axis set");
		}

		List<MoxlNode> newSet = new ArrayList<MoxlNode>();

		int contextSize = baseSet.size();
		for (int contextPosition = 1; contextPosition <= contextSize; contextPosition++)
		{
			MoxlNode node = baseSet.get(contextPosition - 1);
			XPathContext childContext = ctx.beget(node, contextPosition, contextSize);
			addNodesViaAxis(childContext, axis, newSet);
		}

		List<MoxlNode> newSetAfterPredicates = newSet;
		if (predicates != null)
		{
			for (Predicate predicate : predicates)
			{
				try
				{
					newSetAfterPredicates = predicate.filterSet(ctx, newSetAfterPredicates);
				}
				catch (XPathEvaluationException e)
				{
					throw new XPathEvaluationException("Failure during predicate: " + predicate, e);
				}
			}
		}
		return newSetAfterPredicates;
	}

	private void addNodesViaAxis(XPathContext ctx, XPathAxis axis, List<MoxlNode> newSet) throws InvalidAxisForNodeException
	{
		for (MoxlNode node : axis.getNodes(ctx.getContextNode(), this.getNodeTestMatcher()))
		{
			// TODO: filter through predicates, incrementing XPathContext counter
			newSet.add(node);
		}
	}

}
